package com.iisigroup.screenshot;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
import org.json.JSONException;
import org.json.JSONObject;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.Platform;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxProfile;
import org.openqa.selenium.remote.Augmenter;
import org.openqa.selenium.remote.BrowserType;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.CommandExecutor;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.HttpCommandExecutor;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.iisigroup.screenshot.constant.SeleniumConfig;

/**
 * The Class WebUtils.
 */
public class WebUtils {

	/** The Constant logger. */
	private static final Logger LOGGER = LoggerFactory
			.getLogger(WebUtils.class);

	/**
	 * Accept alert and get its text.
	 *
	 * @param driver
	 *            the driver
	 * @return the string
	 */
	public static String acceptAlertAndGetItsText(final WebDriver driver) {
		try {
			final Alert alert = driver.switchTo().alert();
			final String alertText = alert.getText();
			alert.accept();
			return alertText;
		} catch (org.openqa.selenium.NoAlertPresentException e) {
			return null;
		} catch (Exception e) {
			LOGGER.error(e.getMessage(), e);
			// Modal dialog showed
			return null;
		}
	}

	/**
	 * 讓捲軸上下跑.
	 *
	 * @param driver
	 *            the driver
	 */
	public static void scroolbarDownUp(final WebDriver driver) {
		driver.switchTo().defaultContent();
		// Following is the code that scrolls through the page
		for (int second = 0;; second++) {
			if (second >= 3) {
				break;
			}
			((RemoteWebDriver) driver).executeScript("window.scrollBy(0,200)",
					"");
			driver.manage()
					.timeouts()
					.pageLoadTimeout(SeleniumConfig.waitForPageToLoadS,
							TimeUnit.SECONDS);
		}
		for (int second = 0;; second++) {
			if (second >= 10) {
				break;
			}
			((RemoteWebDriver) driver).executeScript("window.scrollBy(0,-200)",
					"");
			driver.manage()
					.timeouts()
					.pageLoadTimeout(SeleniumConfig.waitForPageToLoadS,
							TimeUnit.SECONDS);

		}
	}

	/**
	 * 讓捲軸下跑.
	 *
	 * @param driver
	 *            the driver
	 */
	public static void scroolbarDown(final WebDriver driver) {
		driver.switchTo().defaultContent();
		// Following is the code that scrolls through the page
		for (int second = 0;; second++) {
			if (second >= 3) {
				break;
			}
			((RemoteWebDriver) driver).executeScript("window.scrollBy(0,200)",
					"");

		}
	}

	/**
	 * 讓捲軸上跑.
	 *
	 * @param driver
	 *            the driver
	 */
	public static void scroolbarUp(final WebDriver driver) {
		driver.switchTo().defaultContent();
		// Following is the code that scrolls through the page

		for (int second = 0;; second++) {
			if (second >= 10) {
				break;
			}
			((RemoteWebDriver) driver).executeScript("window.scrollBy(0,-200)",
					"");

		}
	}

	/**
	 * Windows machine.
	 *
	 * @return the remote web driver
	 * @throws MalformedURLException
	 *             the malformed url exception
	 */
	public static RemoteWebDriver windowsMachine() throws MalformedURLException {
		final RemoteWebDriver driver = getWindowsMachine(null, "192.168.10.20",
				4444);
		return driver;
	}

	/**
	 * Gets the random platform machine.
	 *
	 * @return the random platform machine
	 * @throws MalformedURLException
	 *             the malformed url exception
	 */
	public static RemoteWebDriver getRandomPlatformMachine()
			throws MalformedURLException {
		final RemoteWebDriver driver = getRandomPlatformMachine(
				"192.168.10.20", 4444);
		return driver;
	}

	/**
	 * Local machine.
	 *
	 * @return the web driver
	 */
	public static WebDriver localMachine() {
//		final WebDriver driver = new FirefoxDriver(configure());
		System.setProperty("webdriver.chrome.driver", "e:/Users/robert/Desktop/chromedriver.exe");
		//https://sites.google.com/a/chromium.org/chromedriver/downloads
		WebDriver driver = new ChromeDriver();
		return driver;
	}

	/**
	 * Configure.
	 *
	 * @return the desired capabilities
	 */
	public static DesiredCapabilities configure() {
		final DesiredCapabilities capabilities = new DesiredCapabilities();
		capabilities.setCapability(CapabilityType.ACCEPT_SSL_CERTS, true);
		FirefoxProfile profile;

		profile = (FirefoxProfile) capabilities
				.getCapability(FirefoxDriver.PROFILE);

		if (profile == null)
			profile = new FirefoxProfile();

		// enable access to XPCComponents
		profile.setPreference("signed.applets.codebase_principal_support", true);

		// 設定pop最多出現80次
		profile.setPreference("dom.popup_maximum", 80);
		capabilities.setCapability(FirefoxDriver.PROFILE, profile);
		capabilities.setCapability("screenrecorder", true);
		capabilities.setCapability("screenshot", true);
		return capabilities;
	}

	/**
	 * Gets the windows machine.
	 *
	 * @param spcificIp
	 *            the spcific ip
	 * @param seleniumServerUrl
	 *            the selenium server url
	 * @param seleniumServerPort
	 *            the selenium server port
	 * @return the windows machine
	 * @throws MalformedURLException
	 *             the malformed url exception
	 */
	public static RemoteWebDriver getWindowsMachine(final String spcificIp,
			final String seleniumServerUrl, final int seleniumServerPort)
			throws MalformedURLException {
		final RemoteWebDriver driver = getMachine(Platform.WINDOWS, spcificIp,
				seleniumServerUrl, seleniumServerPort);
		return driver;
	}

	/**
	 * Linux machine.
	 *
	 * @return the remote web driver
	 * @throws MalformedURLException
	 *             the malformed url exception
	 */
	public static RemoteWebDriver linuxMachine() throws MalformedURLException {
		final RemoteWebDriver driver = getLinucMachine(null, "192.168.10.20",
				4444);
		return driver;
	}

	/**
	 * Gets the linuc machine.
	 *
	 * @param spcificIp
	 *            the spcific ip
	 * @param seleniumServerUrl
	 *            the selenium server url
	 * @param seleniumServerPort
	 *            the selenium server port
	 * @return the linuc machine
	 * @throws MalformedURLException
	 *             the malformed url exception
	 */
	public static RemoteWebDriver getLinucMachine(final String spcificIp,
			final String seleniumServerUrl, final int seleniumServerPort)
			throws MalformedURLException {
		final RemoteWebDriver driver = getMachine(Platform.LINUX, spcificIp,
				seleniumServerUrl, seleniumServerPort);
		return driver;
	}

	/**
	 * Gets the machine.
	 * 
	 * http://127.0.0.1:5555/wd/hub/static/resource/hub.html
	 *
	 * @param platmform
	 *            the platmform
	 * @param spcificIp
	 *            the spcific ip
	 * @param seleniumServerUrl
	 *            the selenium server url
	 * @param seleniumServerPort
	 *            the selenium server port
	 * @return the machine
	 * @throws MalformedURLException
	 *             the malformed url exception
	 */
	public static RemoteWebDriver getMachine(final Platform platmform,
			final String spcificIp, final String seleniumServerUrl,
			final int seleniumServerPort) throws MalformedURLException {
		final URL remoteAddress = new URL("http://" + seleniumServerUrl + ":"
				+ seleniumServerPort + "/wd/hub");
		// have tried using the below commented out lines as well, but in all
		// cases I face errors.
		// URL remoteAddress = new URL("http://mymachine:4444/grid/register");
		// URL remoteAddress = new URL("http://mymachine:4444/wd/hub");
		final DesiredCapabilities dc = DesiredCapabilities.firefox();
		dc.setBrowserName(BrowserType.FIREFOX);
		final FirefoxProfile profile = new FirefoxProfile();
		// 設定pop最多出現80次
		profile.setPreference("dom.popup_maximum", 80);
		// ProfilesIni allProfiles = new ProfilesIni();
		// final FirefoxProfile profile = allProfiles.getProfile("Selenium") ;
		dc.setCapability(FirefoxDriver.PROFILE, profile);
		dc.setCapability("screenrecorder", true);
		dc.setCapability("screenshot", true);
		// dc.setCapability("applicationCacheEnabled", false);

		if (StringUtils.isNotBlank(spcificIp)) {
			// 交代指明要遙控哪一台機器
			final String spificUrl = "http://" + spcificIp + ":5555";
			// ex: http://192.168.9.47:5555
			dc.setCapability("id", spificUrl);
		}

		final RemoteWebDriver driver = new RemoteWebDriver(remoteAddress, dc);
		LOGGER.info("the Ip of under control's browser: {} ",
				getIPOfNode(driver));
		return driver;
	}

	/**
	 * Gets the machine.
	 * 
	 * http://127.0.0.1:5555/wd/hub/static/resource/hub.html
	 *
	 * @param seleniumServerUrl
	 *            the selenium server url
	 * @param seleniumServerPort
	 *            the selenium server port
	 * @return the machine
	 * @throws MalformedURLException
	 *             the malformed url exception
	 */
	public static RemoteWebDriver getRandomPlatformMachine(
			final String seleniumServerUrl, final int seleniumServerPort)
			throws MalformedURLException {
		final URL remoteAddress = new URL("http://" + seleniumServerUrl + ":"
				+ seleniumServerPort + "/wd/hub");
		// have tried using the below commented out lines as well, but in all
		// cases I face errors.
		// URL remoteAddress = new URL("http://mymachine:4444/grid/register");
		// URL remoteAddress = new URL("http://mymachine:4444/wd/hub");
		final DesiredCapabilities dc = DesiredCapabilities.firefox();

		dc.setBrowserName(BrowserType.FIREFOX);
		final FirefoxProfile profile = new FirefoxProfile();
		// 設定pop最多出現80次
		profile.setPreference("dom.popup_maximum", 80);
		// ProfilesIni allProfiles = new ProfilesIni();
		// final FirefoxProfile profile = allProfiles.getProfile("Selenium") ;
		dc.setCapability(FirefoxDriver.PROFILE, profile);
		dc.setCapability("screenrecorder", true);
		dc.setCapability("screenshot", true);
		// dc.setCapability("applicationCacheEnabled", false);

		final RemoteWebDriver driver = new RemoteWebDriver(remoteAddress, dc);
		LOGGER.info("the Ip of under control's browser: {} ",
				getIPOfNode(driver));
		return driver;
	}

	/**
	 * Retrieve local ips. 得到腳本發送端(遙控端) ip 資訊
	 * 
	 * @return the list
	 */
	public static String getIpv4() {
		try {
			final Enumeration<NetworkInterface> interfaces = NetworkInterface
					.getNetworkInterfaces();
			while (interfaces.hasMoreElements()) {
				NetworkInterface current = interfaces.nextElement();
				if (!current.isUp() || current.isLoopback()
						|| current.isVirtual())
					continue;
				Enumeration<InetAddress> addresses = current.getInetAddresses();
				while (addresses.hasMoreElements()) {
					InetAddress current_addr = addresses.nextElement();
					if (current_addr.isLoopbackAddress())
						continue;
					if (current_addr instanceof Inet4Address) {
						return current_addr.getHostAddress();
					}

				}
			}
		} catch (SocketException e) {
			LOGGER.error(e.getMessage(), e);

		}
		return null;
	}

	/**
	 * 可以得到是遙控哪一個node的ip.
	 *
	 * @param remoteDriver
	 *            the remote driver
	 * @return the IP of node
	 */
	public static String getIPOfNode(RemoteWebDriver remoteDriver) {
		String hostFound = null;
		try {

			final CommandExecutor tmpCe = remoteDriver.getCommandExecutor();
			if (tmpCe instanceof HttpCommandExecutor) {
				final HttpCommandExecutor ce = (HttpCommandExecutor) remoteDriver
						.getCommandExecutor();
				final String hostName = ce.getAddressOfRemoteServer().getHost();
				final int port = ce.getAddressOfRemoteServer().getPort();
				// 使用http post 問 grid server (使用grid api
				// https://github.com/nicegraham/selenium-grid2-api )
				/**
				 * /grid/console/ /grid/beta/console/ /grid/register/
				 * /grid/driver/ /wd/hub/ /selenium-server/driver/
				 * /grid/resources/ /grid/api/proxy/ /grid/api/hub/
				 * /grid/api/testsession/ /lifecycle-manager/
				 * 
				 * ***/
				final HttpHost host = new HttpHost(hostName, port);
				final CloseableHttpClient client = HttpClients.createDefault();
				final String urlTemplate = "http://%s:%s/grid/api/testsession?session=%s ";

				final URL sessionURL = new URL(String.format(urlTemplate,
						hostName, port, remoteDriver.getSessionId()));
				final BasicHttpEntityEnclosingRequest r = new BasicHttpEntityEnclosingRequest(
						"POST", sessionURL.toExternalForm());
				final HttpResponse response = client.execute(host, r);

				final JSONObject object = extractObject(response);
				final URL myURL = new URL(object.getString("proxyId"));
				if ((myURL.getHost() != null) && (myURL.getPort() != -1)) {
					hostFound = myURL.getHost();
				}
			} else if ("org.openqa.selenium.firefox.FirefoxDriver.LazyCommandExecutor"
					.equals(tmpCe.getClass().getCanonicalName())) {
				LOGGER.info("use org.openqa.selenium.firefox.FirefoxDriver.LazyCommandExecutor");
				hostFound = getIpv4();
			}

		} catch (Exception e) {
			LOGGER.error(e.getMessage(), e);
		}
		return hostFound;
	}

	/**
	 * Extract object.
	 *
	 * @param resp
	 *            the resp
	 * @return the JSON object
	 * @throws IOException
	 *             Signals that an I/O exception has occurred.
	 * @throws JSONException
	 *             the JSON exception
	 */
	private static JSONObject extractObject(HttpResponse resp)
			throws IOException, JSONException {
		InputStream contents = resp.getEntity().getContent();
		StringWriter writer = new StringWriter();
		IOUtils.copy(contents, writer, "UTF8");
		JSONObject objToReturn = new JSONObject(writer.toString());
		return objToReturn;
	}

	/**
	 * Handle rl alert.
	 *
	 * @param driver
	 *            the driver
	 * @return true, if successful
	 */
	public static boolean handleRLAlert(final WebDriver driver) {
		boolean giveUpOperation = false;
		driver.manage().timeouts().pageLoadTimeout(30, TimeUnit.SECONDS);

		int count = 0;
		while (true) {
			String errorMessage = driver
					.findElement(
							By.xpath("//*[@id='growl2_container']/div/div/div[2]/span"))
					.getText();
			String errorExtMessage = driver.findElement(
					By.xpath("//*[@id='growl2_container']/div/div/div[2]/p"))
					.getText();
			LOGGER.info(errorMessage);
			LOGGER.info(errorExtMessage);
			if (count > 3) {
				giveUpOperation = true;
				break;
			}
			count++;
		}

		return giveUpOperation;
	}

	/**
	 * Take screen.
	 *
	 * @param driver
	 *            the driver
	 * @param outputFile
	 *            the output file
	 */
	public static void takeScreen(final WebDriver driver, final File outputFile) {

		try {
			if (driver instanceof FirefoxDriver) {
				File scrFile = ((TakesScreenshot) driver)
						.getScreenshotAs(OutputType.FILE);
				FileUtils.copyFile(scrFile, outputFile);
			} else {
				WebDriver augmentedDriver = new Augmenter().augment(driver);
				File scrFile = ((TakesScreenshot) augmentedDriver)
						.getScreenshotAs(OutputType.FILE);
				FileUtils.copyFile(scrFile, outputFile);
			}
			LOGGER.info("takeScreen: {}", outputFile.getCanonicalPath());

		} catch (Exception e) {
			LOGGER.error(e.getMessage(), e);
		}
	}

	/**
	 * Take screen.
	 *
	 * @param driver
	 *            the driver
	 * @param clazz
	 *            the clazz
	 */
	public static void takeScreen(final WebDriver driver, final Class<?> clazz) {
		final SimpleDateFormat sdf = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
		final StringBuilder sbf = new StringBuilder();
		final String[] args = new String[] { System.getProperty("user.home"),
				File.separator, "SeleniumExceptionPNG", File.separator,
				clazz.getSimpleName(), File.separator, sdf.format(new Date()) };
		for (int i = 0; i < args.length; ++i) {
			sbf.append("%s");
		}
		final String fileName = String.format(sbf.append(".png").toString(),
				args);
		final File outputFile = new File(fileName);
		takeScreen(driver, outputFile);
	}

	/**
	 * Gets the main url.
	 *
	 * @param src
	 *            the src
	 * @return the main url
	 */
	public static String getMainUrl(final String src) {
		final String expr = "([a-z][a-z0-9+\\-.]*:(//[^/?#]+)?)";
		final Collection<String> intData = extract(expr, src);
		return (String) CollectionUtils.get(intData, 0);
	}

	/**
	 * Extract.
	 *
	 * @param expr
	 *            the expr
	 * @param src
	 *            the src
	 * @return the sets the
	 */
	public static Set<String> extract(final String expr, final String src) {
		final Set<String> result = new HashSet<String>();
		final Pattern pattern = Pattern.compile(expr);
		final Matcher matcher = pattern.matcher(src);
		while (matcher.find()) {
			String data = matcher.group();
			result.add(data);
		}
		return result;
	}
}
